/*
 * #%L
 * java model
 * %%
 * Copyright (C) 2009 - 2011 prefabware.com Stefan Isele
 * %%
 * All Rights Reserved.
 * This program is dual-licensed under the GNU General Public license
 * or a commercial license.
 * You may select to use one or the other of these licenses.
 * To obtain a commercial license please contact Stefan Isele at www.stefan-isele.de
 * Any modifications to this file must keep this entire header intact.
 * Redistribution, except as permitted by the selected license, is prohibited.
 * #L%
 */
package com.prefabware.jmodel.typefactory;

import java.lang.reflect.Constructor;
import java.util.List;

import com.prefabware.commons.QualifiedName;
import com.prefabware.jmodel.JArgument;
import com.prefabware.jmodel.JClass;
import com.prefabware.jmodel.JClassDeclaration;
import com.prefabware.jmodel.JConstructor;
import com.prefabware.jmodel.JConstructorDeclaration;
import com.prefabware.jmodel.JInterface;
import com.prefabware.jmodel.JModifiers;
import com.prefabware.jmodel.JType;
import com.prefabware.jmodel.JTypeDeclaration;
import com.prefabware.jmodel.JVisibility;

public class JClassTypeFactoryDelegee extends TypeFactoryDelegeeBase implements TypeFactoryDelegee {


	public class JClassClassVisitor extends JTypeClassVisitor {
		public  JClassClassVisitor(TypeFactory typeFactory, JType[] typeArguments) {
			super(typeFactory,typeArguments);
		}

		public JClass getJType() {
			return (JClass) super.getJType();
		}

		@Override
		public void visitConstructor(Constructor<?> constructor) {
			int mod = constructor.getModifiers();
			JModifiers modifiers = JModifiers.getModifier(mod);
			JConstructorDeclaration dcl = new JConstructorDeclaration(JConstructor.getQualifiedName(getJType()), getJType(), modifiers, JVisibility.getVisibility(mod),null);
			JConstructor jc = new JConstructor(dcl, getJType());
			dcl.setDeclaringType(this.getJType());
			jc.setDeclaringType(this.getJType());
			Class<?>[] parameterTypes = constructor.getParameterTypes();
			List<JArgument> arguments = calcArguments(parameterTypes,dcl);
			jc.getArguments().addAll(arguments);
			getJType().add(jc);
		}

		@Override
		public void visitClass(Class<?> clazz) {
			super.visitClass(clazz);
			Class<?> superclass = clazz.getSuperclass();
			if (superclass!=null) {
				JClass superType = this.getTypeFactory().getJType(superclass);
				getJType().setExtends(superType);
			}
			Class<?>[] interfaces = clazz.getInterfaces();
			if (interfaces.length>0) {
				for (Class<?> ifc : interfaces) {
					JInterface ifcType = this.getTypeFactory().getJType(ifc);
					getJType().add(ifcType);
				}
			}
		}

		@Override
		protected JType newJType(QualifiedName qualifiedName, JTypeDeclaration declaration, JType... typeArguments) {
			return JClassTypeFactoryDelegee.this.newJType(qualifiedName, declaration, typeArguments);
		}
	}

	protected JClassTypeFactoryDelegee() {
		super();
	}

	@Override
	public boolean acceptType(Class<? extends JType> typeClass, QualifiedName qualifiedName) {
		return typeClass.equals(JClass.class);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <TYPE extends JType> TYPE newJType(QualifiedName qualifiedName, JTypeDeclaration tDcl,
			JType... typeArguments) {	
		JClassDeclaration cDcl = copyTypeDeclaration(tDcl);
		return (TYPE) new JClass(qualifiedName, cDcl);
	}

	protected JClassDeclaration copyTypeDeclaration(JTypeDeclaration tDcl) {
		//use a special declaration-class with a complete asCode-method
		JClassDeclaration cDcl = new JClassDeclaration(tDcl.getQualifiedName(),tDcl.getModifiers(),tDcl.getVisibility(),tDcl.getComponentType());
		cDcl.setTypeArguments(tDcl.getTypeArguments());
		return cDcl;
	}

	@Override
	public JTypeClassVisitor getVisitor(JType... typeArguments) {
		return new JClassClassVisitor(typeFactory,typeArguments);
	}

}
